Micron Document
🎖️GitЯра🎖️

Node / meshtastic / Meshtastic-Android / files / core / common / src / commonMain / kotlin / org / meshtastic / core / common / log / ExpectedCondition.kt

Displaying Raw • Download

core/common/src/commonMain/kotlin/org/meshtastic/core/common/log/ExpectedCondition.kt cea2c3a015c2c3227f13a3ac9bc4e13c35bbd4ee (cea2c3a0) Text, 5.29 KB

T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.core.common.log

Tff7b72import T7ee787co.touchlab.kermit.Severity
Tff7b72import T7ee787kotlinx.coroutines.CancellationException

T8b949e/**
* Marks a [Throwable] as an *expected condition*: a state the app is designed to encounter and recover from, caused by
* the environment rather than by a defect in this code.
*
* Bluetooth switched off, a runtime permission the user has not granted, location services disabled, and a deliberate
* disconnect are all expected conditions. They deserve a log line — and are worth watching as a *rate* — but they are
* not bugs anyone can act on, so they must never be recorded as exceptions in Crashlytics or Datadog RUM. Reporting
* them buries genuine regressions during release triage.
*
* Implement this on exception types whose very existence means "the environment said no". Where an exception type is
* shared between expected and genuine failures, keep the type clean and simply log the expected call site at
* [Severity.Warn] instead. An [ExpectedCondition] is suppressed by both sinks — see [shouldReportAsException]
* (Crashlytics) and [shouldDowngradeForDatadog] (Datadog), which agree here but deliberately differ on cancellation.
*/
Tff7b72interface T56d364ExpectedCondition Tb4b4b4{
T8b949e/**
* Stable, low-cardinality label naming which condition occurred, e.g. `ble-bluetooth-disabled`. This is what makes
* the condition countable as a rate in the log backend instead of an error, so keep it free of addresses, node ids,
* timings and any other per-user detail.
*/
Tff7b72val Te6edf3expectedConditionLabelTb4b4b4: Tffa657String
Tb4b4b4}

T8b949e/** Cause chains are walked with a depth cap so a malformed or self-referential chain cannot spin. */
Tff7b72private Tff7b72const Tff7b72val Te6edf3MAX_CAUSE_DEPTH Tff7b72= T79c0ff1T79c0ff0

T8b949e/**
* Returns the [ExpectedCondition.expectedConditionLabel] of the first [ExpectedCondition] in this throwable's cause
* chain, or `null` when nothing in the chain is an expected condition.
*
* The chain is walked because transport and coroutine machinery routinely wraps the original cause.
*/
Tff7b72fun Te6edf3ThrowableTb4b4b4.Td2a8ffexpectedConditionLabelTb4b4b4(Tb4b4b4)Tb4b4b4: Tffa657String? Tb4b4b4{
Tff7b72var Te6edf3currentTb4b4b4: Te6edf3Throwable? Tff7b72= Tff7b72this
Tff7b72var Te6edf3depth Tff7b72= T79c0ff0
Tff7b72while Tb4b4b4(Te6edf3current Tff7b72!Tff7b72= Tff7b72null Tff7b72&Tff7b72& Te6edf3depth Tff7b72< Te6edf3MAX_CAUSE_DEPTHTb4b4b4) Tb4b4b4{
Tb4b4b4(Te6edf3current Tff7b72as? Te6edf3ExpectedConditionTb4b4b4)Tff7b72?.Te6edf3let Tb4b4b4{
Tff7b72return Tffa657itTb4b4b4.Te6edf3expectedConditionLabel
Tb4b4b4}
Te6edf3current Tff7b72= Te6edf3currentTb4b4b4.Te6edf3cause
Te6edf3depthTff7b72+Tff7b72+
Tb4b4b4}
Tff7b72return Tff7b72null
Tb4b4b4}

T8b949e/** Returns `true` when this throwable, or anything in its cause chain, is an [ExpectedCondition]. */
Tff7b72fun Te6edf3ThrowableTb4b4b4.Td2a8ffisExpectedConditionTb4b4b4(Tb4b4b4)Tb4b4b4: Tffa657Boolean Tff7b72= Te6edf3expectedConditionLabelTb4b4b4(Tb4b4b4) Tff7b72!Tff7b72= Tff7b72null

T8b949e/**
* Whether a log line should become a **Crashlytics** non-fatal.
*
* A log below [Severity.Error] is never reported by either sink, so warn-level logging remains the simplest way to
* record an expected condition without reporting it.
*
* The two sinks deliberately differ on [CancellationException] — see [shouldDowngradeForDatadog].
*/
Tff7b72fun Td2a8ffshouldReportAsExceptionTb4b4b4(Te6edf3severityTb4b4b4: Te6edf3SeverityTb4b4b4, Te6edf3throwableTb4b4b4: Te6edf3Throwable?Tb4b4b4)Tb4b4b4: Tffa657Boolean Tff7b72= Tff7b72when Tb4b4b4{
Te6edf3severity Tff7b72< Te6edf3SeverityTb4b4b4.Te6edf3Error Tff7b72-Tff7b72> Tff7b72false

T8b949e// Top-level only, matching the long-standing Crashlytics behaviour. Deliberately NOT a cause-chain walk:
T8b949e// coroutine machinery routinely attaches a cancellation as the cause of an unrelated genuine failure, and
T8b949e// unwrapping here would silently drop those reports. An ExpectedCondition marker is an explicit statement by
T8b949e// the author about the nature of the condition, so that one is safe to unwrap; an incidental cancellation
T8b949e// buried in a cause chain is not.
Te6edf3throwable Tff7b72is Te6edf3CancellationException Tff7b72-Tff7b72> Tff7b72false

Te6edf3throwableTff7b72?.Te6edf3isExpectedConditionTb4b4b4(Tb4b4b4) Tff7b72=Tff7b72= Tff7b72true Tff7b72-Tff7b72> Tff7b72false

Tff7b72else Tff7b72-Tff7b72> Tff7b72true
Tb4b4b4}

T8b949e/**
* Whether the **Datadog** writer must downgrade an error-level log to `WARN`.
*
* Datadog has no per-call opt-out: its SDK turns *any* log at `ERROR` or above into a RUM error, throwable or not.
* Downgrading the emitted level is the only way to keep something out of RUM error tracking while still emitting the
* log line.
*
* This intentionally does **not** mirror [shouldReportAsException] for [CancellationException]. Crashlytics filters
* cancellations because it is a crash-triage tool; Datadog keeps them because a cancellation logged at error level
* means some call site *swallowed* it instead of rethrowing — broken structured concurrency, and a real bug. That
* asymmetry is what surfaced the swallowed-cancellation defects fixed in #6468, so it is load-bearing: do not "unify"
* the two rules.
*/
Tff7b72fun Td2a8ffshouldDowngradeForDatadogTb4b4b4(Te6edf3severityTb4b4b4: Te6edf3SeverityTb4b4b4, Te6edf3throwableTb4b4b4: Te6edf3Throwable?Tb4b4b4)Tb4b4b4: Tffa657Boolean Tff7b72=
Te6edf3severity Tff7b72>Tff7b72= Te6edf3SeverityTb4b4b4.Te6edf3Error Tff7b72&Tff7b72& Te6edf3throwableTff7b72?.Te6edf3isExpectedConditionTb4b4b4(Tb4b4b4) Tff7b72=Tff7b72= Tff7b72true

Served by rngit 1.5.0 - Generated in 0.05s